import java.util.Timer;
import java.util.TimerTask;

/** Port Class
    CS158A Spring 2010 Project #2
    @author Clare Lanam & Loc Phan
    */

public class Port
{
   /** bridge to which this port is connected */
   private Bridge bridge;
   /** link to which this port sends messages */
   private Link link;
   /** current role of this port */
   private PortRole role;
   /** current state of this port */
   private PortState state;
   /** distance to root */
   private int pathCost;
   /** hello times since last bdpu was received */
   private int hellosSinceLastUpdate;
   /** Timer */
   private Timer timer;
   /** length of listening stages (and learning stage in stp)
    * </br> this can be set between 4,000 - 30,000 (15,000 by default) */
   private static final int FORWARD_DELAY = 15000;
   
   private boolean flag = true;
   
   // ------[CONSTRUCTOR]------ //
   
   /** Default constructor starts all ports in forwarding state.
       @param bridge the bridge to which this port is attached
       */
   
   public Port(Bridge bridge)
   {
      this.bridge = bridge;
      hellosSinceLastUpdate = 0;
      resetToDesignated();
      state = PortState.BLOCKING;
      timer = new Timer();
   }
   
   // ------[ACCESSORS]------ //
   
   public Bridge getBridge() { return bridge; }
   public int getHellosSinceLastUpdate() { return hellosSinceLastUpdate; }
   public Link getLink() { return link; }
   public int getLinkCost() { return link.getPathCost(); }
   public int getPathCost() { if (bridge.isRoot()) return 0; return pathCost; }
   public PortRole getRole() { return role; }
   public PortState getState() { return state; }
   
   // ------[MUTATORS]------ //
   
   public void incHellosSinceUpdate() { hellosSinceLastUpdate++; }
   public void setLink(Link link) { this.link = link; }
   public void setPathCost(int pathCost) { this.pathCost = pathCost; }
   
   // ------[PUBLIC METHODS]------ //
   
   public Bridge getDistalBridge()
   {
      if (link == null)
         return null;
      return link.getDistalBridge(this);
   }
   
   public Port getDistalPort()
   {
      if (link == null)
         return null;
      return link.getDistalPort(this);
   }
   
   public void receive(BPDU bpdu)
   {
      if (bpdu == null)
         return;
      
      hellosSinceLastUpdate = 0; // found a bpdu! yay!
      bridge.receive(this, bpdu);
   }
   
   /** What to do when starting a new connection or if the
       current connection has timed out */
   
   public void resetToDesignated()
   {
      if(bridge.isRoot()) {
         state = PortState.BLOCKING;
      } else {
         getPortReadyForForwarding();
      }
      role = PortRole.DESIGNATED;
      pathCost = Integer.MAX_VALUE;
   }
   
   public void send(BPDU bpdu)
   {
      if (role != PortRole.DESIGNATED)
         return; // nice try :p
      
      if (link != null && bpdu != null)
         link.send(this, bpdu);
   }
   
   public void setRole(PortRole role)
   {
      this.role = role;
            
      if (role == PortRole.ALTERNATE || role == PortRole.BACKUP) {
         timer.purge();
         state = PortState.BLOCKING;
      }
      else {
          getPortReadyForForwarding();
      }
   }
   
   public void setState(PortState state)
   {
      if(state == PortState.BLOCKING)
      this.state = state;
      
      if (state == PortState.FORWARDING || state == PortState.LEARNING)
      {
         if (role == PortRole.ALTERNATE) // flip on
            role = PortRole.ROOT;
         else if (role == PortRole.BACKUP)
            role = PortRole.DESIGNATED;
      }
      else if (state == PortState.BLOCKING)
      {
         if (role == PortRole.ROOT) // flip off
            role = PortRole.ALTERNATE;
         else if (role == PortRole.DESIGNATED)
            role = PortRole.BACKUP;
      }
   }
   
   public boolean isReady() { 
      if(state == PortState.FORWARDING) return true; 
      else return false; 
   }
   private void getPortReadyForForwarding() {
      if(state != PortState.BLOCKING) return;
      
      state = PortState.LISTENING;
      if(flag) {
         TimerTask task1 = new TimerTask() {
            @Override
            public void run()
            {
               try
               {
                  Thread.sleep(FORWARD_DELAY);
                  if (state == PortState.LISTENING)
                     state = PortState.LEARNING;
               } catch (InterruptedException e)
               {
                  e.printStackTrace();
               }
            }
         };
         TimerTask task2 = new TimerTask() {
            @Override
            public void run()
            {
               try
               {
                  Thread.sleep(FORWARD_DELAY);
                  if (state == PortState.LEARNING)
                     state = PortState.FORWARDING;
               } catch (InterruptedException e)
               {
                  e.printStackTrace();
               }
            }
         };
         timer.schedule(task1, 0); 
         timer.schedule(task2, 0);
      }
      flag = false;
   }
}
